/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.remote.grpc.registry;

import io.grpc.BindableService;
import io.grpc.ServerInterceptor;
import io.iec.edp.caf.common.JSONSerializer;
import io.iec.edp.caf.commons.event.StartupCompletedEvent;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.msu.api.ServiceUnitAwareService;
import io.iec.edp.caf.msu.api.client.ServiceRegistry;
import io.iec.edp.caf.msu.api.entity.MsuProperties;
import io.iec.edp.caf.msu.api.entity.ServiceUnitInfo;
import io.iec.edp.caf.msu.api.entity.ServiceUnitRegisterInfo;
import io.iec.edp.caf.rpc.api.grpc.annotation.GrpcClientInterceptor;
import io.iec.edp.caf.rpc.api.grpc.annotation.GrpcServerInterceptor;
import io.iec.edp.caf.rpc.api.grpc.annotation.GrpcService;
import io.iec.edp.caf.rpc.api.grpc.utils.GrpcServiceFactory;
import io.iec.edp.caf.rpc.api.grpc.variable.GrpcVariable;
import io.iec.edp.caf.rpc.remote.grpc.GrpcServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class GrpcServiceRegistryListener implements ApplicationListener<StartupCompletedEvent> {
    //服务注册
    private ServiceRegistry serviceRegistry;

    //微服务设置
    private MsuProperties configuration;

    public GrpcServiceRegistryListener(ServiceRegistry serviceRegistry, MsuProperties configuration) {
        this.serviceRegistry = serviceRegistry;
        this.configuration = configuration;
    }
    @Override
    public void onApplicationEvent(StartupCompletedEvent event) {

        String ip = CafEnvironment.getLocalIp(this.configuration.getIp().getPrefix());

        //获取配置的grpc端口
        String port = CafEnvironment.getEnvironment().getProperty("grpc.server.port");
        if (port == null || "".equals(port)) return;

        //初始化grpc自动装配类（server/interceptor）
        initializeGrpcFactory();

        //组装实例注册信息
        //获取当前应用服务器下的所有SU信息
        ServiceUnitAwareService suAware = SpringBeanUtils.getBean(ServiceUnitAwareService.class);
        ServiceUnitRegisterInfo serviceUnitRegisterInfo = suAware.getServiceUnitRegisterInfo(this.configuration.getApplicationName(), this.configuration.getServiceName());
        ServiceUnitRegisterInfo grpcRegisterInfo = JSONSerializer.deserialize(JSONSerializer.serialize(serviceUnitRegisterInfo), ServiceUnitRegisterInfo.class);
        List<ServiceUnitInfo> serviceUnitInfos = grpcRegisterInfo.getServiceUnitInfo();

        serviceUnitInfos.forEach(info-> info.setName(info.getName()+"_grpc"));

        //注册微服务信息
        this.serviceRegistry.register(serviceUnitRegisterInfo,ip,Integer.valueOf(port));
        GrpcServer grpcServer = new GrpcServer(Integer.parseInt(port));
        //注册服务和服务端连接器
        registerGrpcService(grpcServer);
        registerGrpcServerInterceptor(grpcServer);
        if(!grpcServer.ifRegistered()){
            throw new RuntimeException("There is no service been registered into grpcServer. GrpcServer starts fail.");
        }
        try {
            //服务启动
            grpcServer.buildServer();
            grpcServer.start();
            log.info("GrpcService has already start on port: "+port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void  initializeGrpcFactory(){
        ApplicationContext context = SpringBeanUtils.getApplicationContext();
        Map<String,Object> grpcServices = context.getBeansWithAnnotation(GrpcService.class);
        Map<String,Object> grpcClientFilter = context.getBeansWithAnnotation(GrpcClientInterceptor.class);
        Map<String,Object> grpcServerFilter = context.getBeansWithAnnotation(GrpcServerInterceptor.class);

        Map<String,Map<String,Object>> serviceResult = new HashMap<>();
        Map<String,Map<String,Object>> clientResult = new HashMap<>();
        Map<String,Map<String,Object>> serverResult = new HashMap<>();

        grpcServices.forEach((beanName,bean)->{
            String mode = bean.getClass().getAnnotation(GrpcService.class).mode();
            if(serviceResult.containsKey(mode)){
                serviceResult.get(mode).put(beanName,bean);
            }else{
                Map<String,Object> map = new HashMap<>();
                map.put(beanName,bean);
                serviceResult.put(mode,map);
            }
        });

        grpcClientFilter.forEach((beanName,bean)->{
            String mode = bean.getClass().getAnnotation(GrpcClientInterceptor.class).mode();
            if(clientResult.containsKey(mode)){
                clientResult.get(mode).put(beanName,bean);
            }else{
                Map<String,Object> map = new HashMap<>();
                map.put(beanName,bean);
                clientResult.put(mode,map);
            }
        });

        grpcServerFilter.forEach((beanName,bean)->{
            String mode = bean.getClass().getAnnotation(GrpcServerInterceptor.class).mode();
            if(serverResult.containsKey(mode)){
                serverResult.get(mode).put(beanName,bean);
            }else{
                Map<String,Object> map = new HashMap<>();
                map.put(beanName,bean);
                serverResult.put(mode,map);
            }
        });

        GrpcServiceFactory.initialize(serviceResult,clientResult,serverResult);

    }

    private void registerGrpcService(GrpcServer grpcServer){
        Map<String,Object> grpcServices = GrpcServiceFactory.getServices(GrpcVariable.CAF_GRPC_MODE_DEFAULT);
        if(grpcServices!=null){
            grpcServices.forEach((beanName,bean)->{
                grpcServer.registerGrpcService((BindableService) bean);
                log.info( beanName + " has already registered into grpcService.");
            });
        }
    }

    private void registerGrpcServerInterceptor(GrpcServer grpcServer){
        Map<String,Object> grpcServerFilters = GrpcServiceFactory.getServerFilter(GrpcVariable.CAF_GRPC_MODE_DEFAULT);
        if(grpcServerFilters!=null){
            grpcServerFilters.forEach((beanName,bean)->{
                grpcServer.registerInterceptor((ServerInterceptor) bean);
                log.info( beanName + " has already registered into grpcServerInterceptors.");
            });
        }
    }
}
